/*   Copyright © 2009 Mark Gil Torres
 *
 *   This file is part of mtoolkit.
 *
 *   mtoolkit is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   any later version.
 *
 *   mtoolkit is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with mtoolkit.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MTA_PUSH_H
#define MTA_PUSH_H

#include <iostream>
#include <string>
#include <stack>
#include <boost/lexical_cast.hpp>

//#include "../include/integer.h"
//#include "../include/rational.h"
//#include "../include/real.h"
#include "variant.hpp"
#include "../include/q.hpp"
#include "../include/variable.hpp"
#include "../include/term.hpp"
#include "../include/algebraicExpression.hpp"

using namespace mt;
using namespace std;

/*
void displayAE(stack<Variant>& myStack) {
    AlgebraicExpression<Q<int> > x;
    Variant temp;
    temp = myStack.top();
    x = temp.getAIValue();
    vector<Term<Q<int> > > z;
    z = x.getAE();
    Term<Q<int> > m;
    Q<int> zero = 0;
//    cout<< z.size();
    for (int i =0; i<z.size();i++) {
      m= z[i];
      if (i>0) {
        if (m.getCoefficient() > zero) cout << " + " << m.getCoefficient();
      }
      else cout << (m.getCoefficient());
      vector<Variable<Q<int> > > b = m.getVariable();
      Variable<Q<int> > temp1;
      for (int i=0; i < b.size(); i++) {
        temp1 = b[i];
        cout << temp1.getName() << "^{" << temp1.getExponent() << "}";
      }
    }
    cout << "\nmt# ";
}
*/

struct pushZ {
  pushZ(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant temp;
    Q<int> a(boost::lexical_cast<int>(s));
//    a.setNumerator(boost::lexical_cast<int>(s));
    temp.setQIValue(a);
    myStack.push(temp);
  }
  stack<Variant>& myStack;
};

struct pushZi {
  pushZi(stack<Variant>& inStack) : myStack(inStack) {}

  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant temp;
    temp.setIValue(boost::lexical_cast<int>(s));
    myStack.push(temp);
// for testing puposes
//    cout<<"PUSHZ : "<<s<<endl;
  }
  stack<Variant>& myStack;
};

struct pushQ {
  pushQ(stack<Variant>& inStack) : myStack(inStack) {}

  void operator()(char const* str, char const* end) const {
    Variant x, y, z;
    int a, b;
    y = myStack.top();
    b = y.getIValue();
    myStack.pop();
    x = myStack.top();
    a = x.getIValue();
    myStack.pop();
    Q<int> c(a,b);
//    c.setNumerator(a);
//    c.setDenominator(b);
    z.setQIValue(c);
    myStack.push(z);
  }
  stack<Variant>& myStack;
};

struct pushVarName {
  pushVarName(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant temp;
    temp.setSValue(s);
    temp.setID(1);
    myStack.push(temp);
  }
  stack<Variant>& myStack;
};

struct pushVarExp {
  pushVarExp(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant temp;
    temp = myStack.top();
    myStack.pop();
    temp.setID(2);
    myStack.push(temp);
  }
  stack<Variant>& myStack;
};

struct pushVarSub {
  pushVarSub(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant temp;
    temp.setSValue(s);
    temp.setID(3);
    myStack.push(temp);
  }
  stack<Variant>& myStack;
};

struct pushVar {
  pushVar(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    int idTemp;
    string sTemp;
    Variant y, z;
    Variable<Q<int> > a;
    Q<int> b;
    z = myStack.top();
    myStack.pop();
    idTemp = z.getID();
    while (idTemp != -1) {
      if(idTemp == 3) {
        sTemp = z.getSValue();
        a.setSubscript(sTemp);
      }
      if(idTemp == 2) {
        b = z.getQIValue();
        a.setExponent(b);
      }
      if(idTemp == 1) {
        sTemp = z.getSValue();
        a.setName(sTemp);
        break;
      }
      z = myStack.top();
      myStack.pop();
      idTemp = z.getID();
    }
    y.setVIValue(a);
    y.setID(5);

/*
    Variable<Q<int> > g;
    g = y.getVIValue();
    cout<< g.getName();
    Q<int> zero(0);
    Q<int> one(1);
    if (g.getExponent() != zero && g.getExponent() != one) cout<<"^{"<<g.getExponent()<<"}";
    if (g.getSubscript() != "NULL") cout<<"_{"<<g.getSubscript()<<"}";
*/

    myStack.push(y);
  }
  stack<Variant>& myStack;
};

struct pushTermConst {
  pushTermConst(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant temp;
    temp = myStack.top();
    myStack.pop();
    temp.setID(4);

/*
    Q<int> qTemp;
    qTemp = temp.getQIValue();
    if (qTemp.isInteger()) {
      cout << "TermConst = " << qTemp.getNumerator();
    }
    else {
      cout << "TermConst = " << qTemp.getNumerator() << "/" << qTemp.getDenominator();
    }
    cout << endl;
*/

    myStack.push(temp);
  }
  stack<Variant>& myStack;
};

struct pushTerm {
  pushTerm(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant y, z;
    int idTemp;
    Term<Q<int> > termTemp;
    vector<Variable<Q<int> > > vecTemp;
    Variable<Q<int> > varTemp;
    Q<int> constTemp(1);
    termTemp.setCoefficient(constTemp);
    z = myStack.top();
    idTemp = z.getID();
    while (idTemp == 4 || idTemp == 5) {
      myStack.pop();
      if(idTemp == 5) {
        varTemp = z.getVIValue();
        vecTemp.insert(vecTemp.begin(),1,varTemp);
        termTemp.setVariable(vecTemp);
      }
      if(idTemp == 4) {
        constTemp = z.getQIValue();
        termTemp.setCoefficient(constTemp);
        break;
      }
      if(myStack.empty()) break;
      z = myStack.top();
      idTemp = z.getID();
    }
    y.setTIValue(termTemp);
    y.setID(6);

/*
    Q<int> qTemp;
    Variable<Q<int> > vTemp;
    Term<Q<int> > tTemp;
    vector<Variable<Q<int> > > vvTemp;
    tTemp = y.getTIValue();
    qTemp = tTemp.getCoefficient();
    if (qTemp.isInteger()) {
      cout << qTemp.getNumerator();
    }
    else {
      cout << "\frac{" << qTemp.getNumerator() << "}{" << qTemp.getDenominator() << "}";
    }
    vvTemp = tTemp.getVariable();
    for(int i = 0; i<vvTemp.size(); i++){
    vTemp = vvTemp[i];
    cout<< vTemp.getName();
    Q<int> zero(0);
    Q<int> one(1);
    if (vTemp.getExponent() != zero && vTemp.getExponent() != one) cout<<"^{"<<vTemp.getExponent()<<"}";
    if (vTemp.getSubscript() != "NULL") cout<<"_{"<<vTemp.getSubscript()<<"}";
    }
    cout<<endl;
*/

    myStack.push(y);
  }
  stack<Variant>& myStack;
};

struct pushAE {
  pushAE(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    Variant y, z;
    int size = 0;
    int idTemp = 0;
    AlgebraicExpression<Q<int> > aeTemp;
    vector<Term<Q<int> > > vecTemp;
    vecTemp.clear();
    Term<Q<int> > termTemp;
    z = myStack.top();
    cout << "idTemp = " << z.getID() <<endl;
    idTemp = z.getID();
    while (idTemp == 6) {
      myStack.pop();
      size++;
      termTemp = z.getTIValue();
      vecTemp.insert(vecTemp.begin(),1,termTemp);
      if(myStack.empty()) break;
      z = myStack.top();
      idTemp = z.getID();
      cout << "idTemp = " << idTemp <<endl;
    }
    aeTemp.setAE(vecTemp);
    cout<<"Size is : " << size << endl;
    y.setAIValue(aeTemp);
    y.setID(7);

    AlgebraicExpression<Q<int> > aTemp;
//    vector<Term<Q<int> > > vaTemp;
    vector<Term<Q<int> > > vaTemp(0);
    Q<int> qTemp;
    Variable<Q<int> > vTemp;
    Term<Q<int> > tTemp;
    vector<Variable<Q<int> > > vvTemp;
    Q<int> zero(0);
    Q<int> one(1);
    aTemp = y.getAIValue();
    vaTemp = aTemp.getAE();
    for(int j=0;j<vaTemp.size();j++) {
      tTemp = vaTemp[j];
      qTemp = tTemp.getCoefficient();
      if(qTemp == zero) continue;
      if(qTemp >= zero && j > 0) cout<<"+";
      if (qTemp.isInteger()) {
        cout << qTemp.getNumerator();
      }
      else {
        cout << "\frac{" << qTemp.getNumerator() << "}{" << qTemp.getDenominator() << "}";
      }
      vvTemp = tTemp.getVariable();
      for(int i = 0; i<vvTemp.size(); i++){
        vTemp = vvTemp[i];
        cout<< vTemp.getName();
        if (vTemp.getExponent() != zero && vTemp.getExponent() != one) cout<<"^{"<<vTemp.getExponent()<<"}";
        if (vTemp.getSubscript() != "NULL") cout<<"_{"<<vTemp.getSubscript()<<"}";
      }
    }
    cout<<endl;


    myStack.push(y);
//    displayAE(myStack);
  }
  stack<Variant>& myStack;
};


/*
struct displayAE {
  displayAE(stack<Variant>& inStack) : myStack(inStack) {}
  void operator()(char const* str, char const* end) const {
    string s(str,end);
    AlgebraicExpression<Q<int> > x;
    Variant temp;
    temp = myStack.top();
    x = temp.getAIValue();
    vector<Term<Q<int> > > z;
    z = x.getAE();
    Term<Q<int> > m;
    Q<int> zero = 0;
    for (int i =0; i<z.size();i++) {
      m= z[i];
      if (i>0) {
        if (m.getCoefficient() > zero) cout << " + " << m.getCoefficient();
      }
      else cout << (m.getCoefficient());
      vector<Variable<Q<int> > > b = m.getVariable();
      Variable<Q<int> > temp1;
      for (int i=0; i < b.size(); i++) {
        temp1 = b[i];
        cout << temp1.getName() << "^{" << temp1.getExponent() << "}";
      }
    }
    cout << "\nmt# ";
  }
  stack<Variant>& myStack;
};

struct doAssign {
  doAssign(Variant*& inVar, stack<Variant>& inStack) : var(inVar), myStack(inStack) {}
  void operator()(char const*, char const*) const {
    if (var != 0) {
      *var = myStack.top();
    }
  }
  Variant*& var;
  stack<Variant>& myStack;
};
*/

#endif
